/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using DaveSexton.DocProject.Engine;
using System.Reflection;
using DaveSexton.DocProject.MSHelpCompiler;
using DaveSexton.DocProject.Sandcastle.Resources;

namespace DaveSexton.DocProject.Sandcastle
{
	internal sealed class Help2CompilerStep : BuildStep<SandcastleBuildEngine>
	{
		#region Public Properties
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")]
		public static readonly string DefaultName = "Compile Help 2.x";

		public override string Name
		{
			get
			{
				return DefaultName;
			}
		}

		public static bool Help2APILoaded
		{
			[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]
			get
			{
				return Help2CompilerLoader.Instance.TryLoadAssembly();
			}
		}
		#endregion

		#region Private / Protected
		private volatile bool canceled, error;
		private readonly string projectFile;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="Help2CompilerStep" /> class.
		/// </summary>
		public Help2CompilerStep(SandcastleBuildEngine engine, string projectFile)
			: base(engine)
		{
			if (projectFile == null)
				throw new ArgumentNullException("projectFile");

			IncludeInPartialBuild = true;
			RunInBackground = true;

			this.projectFile = projectFile;
		}
		#endregion

		#region Methods
		protected override void ExecuteInternal(BuildContext context)
		{
			try
			{
				context.TraceLine();

				HxComp compiler = (HxComp) new HxCompClass();
				compiler.Initialize();

				HxCompError callback = new HxCompError(this, compiler);
				callback.Attach();

				string target = Engine.Settings.CompiledHelp2xFile;
				string targetDir = System.IO.Path.GetDirectoryName(target);

				if (!System.IO.Directory.Exists(targetDir))
					System.IO.Directory.CreateDirectory(targetDir);

				try
				{
					compiler.Compile(projectFile, null, target, 0);
				}
				catch (ArgumentException ex)
				// thrown during testing when the .HxC file doesn't exist
				{
					throw new InvalidOperationException(Errors.Help2CompilerArgumentException, ex);
				}
				finally
				{
					callback.Detach();
				}

				if (error)
					throw new InvalidOperationException(Errors.Help2CompilerError);
			}
			finally
			{
				canceled = false;

				GC.Collect();
			}
		}

		protected override void CancelInternal(BuildContext context)
		{
			canceled = true;
		}
		#endregion

		#region Nested
		private sealed class HxCompError : IHxCompError
		{
			private readonly Help2CompilerStep step;
			private readonly HxComp compiler;
			private int adviseHandle;

			public HxCompError(Help2CompilerStep step, HxComp compiler)
			{
				this.step = step;
				this.compiler = compiler;
			}

			#region Methods
			public void Attach()
			{
				adviseHandle = compiler.AdviseCompilerMessageCallback(this);
			}

			public void Detach()
			{
				compiler.UnadviseCompilerMessageCallback(adviseHandle);
			}
			#endregion

			#region IHxCompError Members
			HxCompStatus IHxCompError.QueryStatus()
			{
				return (step.canceled) ? HxCompStatus.Cancel : HxCompStatus.Continue;
			}

			void IHxCompError.ReportError(string TaskItemString, string Filename, int nLineNum, int nCharNum, HxCompErrorSeverity Severity, string DescriptionString)
			{
				switch (Severity)
				{
					case HxCompErrorSeverity.Information:
						step.CurrentContext.AddInfoToList("Help 2.x Compiler", TaskItemString ?? DescriptionString,
							Filename ?? string.Empty, Math.Max(0, nLineNum), "Info: " + DescriptionString + Environment.NewLine);
						break;
					case HxCompErrorSeverity.Warning:
						step.CurrentContext.AddWarningToList("Help 2.x Compiler", TaskItemString ?? DescriptionString,
							Filename ?? string.Empty, Math.Max(0, nLineNum), "Warn: " + DescriptionString + Environment.NewLine);
						break;
					case HxCompErrorSeverity.Error:
						step.CurrentContext.AddErrorToList("Help 2.x Compiler", TaskItemString ?? DescriptionString,
							Filename ?? string.Empty, Math.Max(0, nLineNum), "Error: " + DescriptionString + Environment.NewLine);
						step.error = true;
						break;
					case HxCompErrorSeverity.Fatal:
						step.CurrentContext.AddErrorToList("Help 2.x Compiler", TaskItemString ?? DescriptionString,
							Filename ?? string.Empty, Math.Max(0, nLineNum), "Fatal Error: " + DescriptionString + Environment.NewLine);
						step.error = true;
						break;
				}
			}

			void IHxCompError.ReportMessage(HxCompErrorSeverity Severity, string DescriptionString)
			{
				string label = null;

				switch (Severity)
				{
					case HxCompErrorSeverity.Information:
						label = "Info: ";
						break;
					case HxCompErrorSeverity.Warning:
						label = "Warn: ";
						break;
					case HxCompErrorSeverity.Error:
						label = "Error: ";
						break;
					case HxCompErrorSeverity.Fatal:
						label = "Fatal Error: ";
						break;
				}

				step.CurrentContext.TraceLine(label + DescriptionString);
			}
			#endregion
		}
		#endregion
	}
}
